package edu.hawaii.duedates;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.TimerTask;

import org.apache.commons.cli.ParseException;

/**
 * DueDates is an open source application that parses library websites to check for due date
 * information.  The data from the library is retrieved for the user after pertinent information
 * is inputed.  The due date records are returned in a sorted order by date with the book due
 * soonest first.
 * 
 * @author John Ancheta, Daniel Arakaki, Mari-Lee Flestado
 * @version 1.1, October 14, 2008
 */
public class DueDates extends TimerTask {
  
  private CommandLineArgs commandLineArgs = null;
    
  /**
   * The sort information method takes in the bookList as a parameter and places the books'
   * information in chronological order.
   * 
   * @param bookList An ArrayList that holds the information of the book's due date and book title.
   */
  public void sortInformation(List<BookInfoRecord> bookList) {
    Collections.sort(bookList);
  }

  /**
   * Obtain only the information of the book list for items that are due in ndaysTillDue or less.
   * 
   * @param ndaysTillDue The number of days till the item is due.
   * @param bookList The original bookList to check.
   * 
   * @return A bookList for only items that are due in ndaysTillDue or less.
   * 
   * @throws Exception For bookList with date format other than MM-dd-yy.
   */
  public List<BookInfoRecord> getWithinBookList(int ndaysTillDue, List<BookInfoRecord> bookList)
      throws Exception {
    if (ndaysTillDue <= 0) {
      return bookList;
    }
    Calendar fromTimeRange = Calendar.getInstance();
    List<BookInfoRecord> withinBookList = new ArrayList<BookInfoRecord>();
    for (BookInfoRecord pair : bookList) {
      Date dueDate = pair.getDueDate();
      String bookInfo = pair.getBookTitle();
      String libraryCode = pair.getLibrary();
      if (fromTimeRange.getTimeInMillis() > dueDate.getTime()) { // past due
        withinBookList.add(new BookInfoRecord(dueDate, bookInfo, libraryCode));
      }
      else {
        long nmillisecondsTillDue = 86400000L * ndaysTillDue;
        long epochDueDate = dueDate.getTime() - nmillisecondsTillDue;
        long epochFromTimeRange = fromTimeRange.getTime().getTime();
        if (epochDueDate < epochFromTimeRange) {
          withinBookList.add(new BookInfoRecord(dueDate, bookInfo, libraryCode));
        }
      }
    }
    return withinBookList;
  }

  /**
   * The getSiteInfo method gets the information from the command line. The method needs an argument
   * to verify which site it taking information from.
   * 
   * @return A String of the user's information from the getUhmInfo method.
   * 
   * @throws Exception An exception is thrown when an error occurs while retrieving library
   *         information.
   */
  public String getSiteInfo() throws Exception {
    HiStateLibrary hiState = new HiStateLibrary();
    UniversityOfHawaiiLibrary uhmLibrary = new UniversityOfHawaiiLibrary();
    List<BookInfoRecord> bookList = new ArrayList<BookInfoRecord>();

    if (this.commandLineArgs.hasHslOption()) {
      String barcode = this.commandLineArgs.getHslId();
      String pin = this.commandLineArgs.getHslLastFourDigitsForPhoneNumber();
      hiState.getHawaiiStateInfo(barcode, pin, bookList);
    }

    if (this.commandLineArgs.hasUhmOption()) {
      String id = this.commandLineArgs.getUhmId();
      String lastName = this.commandLineArgs.getUhmLastName();
      uhmLibrary.getUhmInfo(id, lastName, bookList);
    }

    int ndaysTillDue = this.commandLineArgs.getWithinValue();
    List<BookInfoRecord> withinBookList = getWithinBookList(ndaysTillDue, bookList);

    if ("duedate".equals(commandLineArgs.getSortValue())) {
      sortInformation(withinBookList);
    }

    //formatted information for uhm and hsl
    String formatter = " %1$-30.30s | %2$-10.10s| %3$20.110s\n";
    String result = String.format(formatter, "Due Date", "Library", "Book Info\n");
    for (BookInfoRecord pair : withinBookList) {
      String library = pair.getLibrary();
      String bookTitle = pair.getBookTitle().replace('\n', ' ');
      String duedate = pair.getDueDate().toString();
      result = result.concat(String.format(formatter, duedate, library, bookTitle));
    }
    return result;
  }
  
  /**
   * Sets member commandLineArgs.
   * 
   * @param commandLineArgs Holds the command line arguments for DueDates.
   */
  public void set(CommandLineArgs commandLineArgs) {
    this.commandLineArgs = commandLineArgs;
  }
  
  /**
   * Overrides the TimerTask run.
   * 
   */
  @Override
  public void run() {
    try {
      DueDatesMail aMailer = DueDatesMail.getInstance();
      String siteInfo = getSiteInfo();
      if (this.commandLineArgs.hasConsoleOption()) {
        System.out.println(siteInfo);
      }
      if (this.commandLineArgs.hasEmailOption()) {       
        String emailAccount = this.commandLineArgs.getEmailAddressValue();
        String smtpServer = this.commandLineArgs.getEmailSmtpServerValue();
        aMailer.sendMail(emailAccount, smtpServer, siteInfo);
      }
    }
    catch (Exception e) {
      if (commandLineArgs.hasVerboseOption()) {
        e.printStackTrace();
      }
      else {
        System.out.println(e.getMessage());
      }
    }
  }
  /**
   * The main method handles the command line arguments and prints a stack trace when invoked.
   * 
   * @param args Command lines for DueDates.
   */
  public static void main(String[] args) {
    CommandLineArgs commandLineArgs = new CommandLineArgs();
    try {
      DueDates dueDates = new DueDates();
      commandLineArgs.parse(args);
      dueDates.set(commandLineArgs);
      double wakeupValue = commandLineArgs.getWakeupValue();
      //If the wakeupValue > 0, the timer will run infinitely until termination else
      //the timer will run once
      if (wakeupValue > 0) {
        ProjectTimer dueDatesTimer = new ProjectTimer();
        dueDatesTimer.schedule(dueDates, wakeupValue); 
      }
      else {
        dueDates.run(); 
      }
    }
    catch (ParseException e) {
      System.out.println(e.getMessage());
      commandLineArgs.printHelp();
    }
  }
}

